using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Text;

namespace Common.Compression
{
    /// <summary>
    /// Helper functions for using Zlib compression.
    /// </summary>
    public static class ZlibHelpers
    {
        /// <summary>
        /// Uncompress a byte buffer.
        /// </summary>
        /// <param name="rgb">Byte buffer compressed in zlib form.</param>
        /// <returns>Uncompressed version of byte buffer.</returns>
        public static byte[] ZlibDecompress(byte[] rgb)
        {
            // make a fixed buffer.
            const int BUFFERSIZE = 4096;
            byte[] buffer = new byte[BUFFERSIZE];

            // keep a list of the things we read.
            List<byte[]> lrgb = new List<byte[]>();
            int iTotalRead = 0;

            // now read all available data.
            using (ZlibStream zs = new ZlibStream(new MemoryStream(rgb), System.IO.Compression.CompressionMode.Decompress))
            {
                while (true)
                {
                    int iRead = zs.Read(buffer, 0, BUFFERSIZE);
                    if (iRead == 0)
                        break;
                    iTotalRead += iRead;
                    lrgb.Add(CopyPrefix(buffer, iRead));
                }
            }

            // flatten down to a single array.
            byte[] rgbOutput = new byte[iTotalRead];
            int iPosition = 0;
            for (int iChunk = 0; iChunk < lrgb.Count; ++iChunk)
            {
                byte[] rgbChunk = lrgb[iChunk];
                int iChunkLength = rgbChunk.Length;
                Array.Copy(rgbChunk, 0, rgbOutput, iPosition, iChunkLength);
                iPosition += iChunkLength;
            }

            Debug.Assert(iPosition == iTotalRead);
            return rgbOutput;
        }

        /// <summary>
        /// Compress a byte buffer using zlib format.
        /// </summary>
        /// <param name="rgb"></param>
        /// <returns></returns>
        public static byte[] ZlibCompress(byte[] rgb)
        {
            // now read all available data.
            using (MemoryStream ms = new MemoryStream())
            {
                using (ZlibStream zs = new ZlibStream(ms, System.IO.Compression.CompressionMode.Compress, true))
                {
                    zs.Write(rgb, 0, rgb.Length);
                }
                return CopyPrefix(ms.GetBuffer(), (int)ms.Length);
            }
        }

        private static byte[] CopyPrefix(byte[] buffer, int iPrefixLen)
        {
            byte[] rgb = new byte[iPrefixLen];
            Array.Copy(buffer, rgb, iPrefixLen);
            return rgb;
        }

        public static byte[] ZlibCompressFaster(byte[] rgb)
        {
            throw new Exception("NYI!");
        }
    }
}
